// wrapped by build app
define("dojox/wire/ml/Action", ["dijit", "dojo", "dojox", "dojo/require!dijit/_Widget,dijit/_Container,dojox/wire/Wire,dojox/wire/ml/util"], function (dijit, dojo, dojox) {
    dojo.provide("dojox.wire.ml.Action");

    dojo.require("dijit._Widget");
    dojo.require("dijit._Container");
    dojo.require("dojox.wire.Wire");
    dojo.require("dojox.wire.ml.util");

    dojo.declare("dojox.wire.ml.Action", [dijit._Widget, dijit._Container], {
        // summary:
        //		A base widget to "run" a task on an event or a topic
        // description:
        //		This widget represents a controller task to be run when an event
        //		(a function) or a topic is issued.
        //		Sub-classes must implement _run() method to implement their tasks.
        //		'trigger' specifies an event scope, an ID of a widget or an DOM
        //		element, or its property with the optional dotted notation.
        //		If this widget has child ActionFilter widgets, their filter()
        //		methods are called with the arguments to the event or the topic.
        //		If one of filter() methods returns false, run() won't be invoked.
        //		This widget also can serve as a composite task to run child
        //		Actions on an event or a topic specified to this widget.
        // trigger:
        //		An event scope
        // triggerEvent:
        //		An event (function) name
        // triggerTopic:
        //		A topic name
        trigger:"",
        triggerEvent:"",
        triggerTopic:"",

        postCreate:function () {
            // summary:
            //		Call _connect()
            // description:
            //		See _connect().
            this._connect();
        },

        _connect:function () {
            // summary:
            //		Connect run() method to an event or a topic
            // description:
            //		If 'triggerEvent' and 'trigger' are specified, connect() is
            //		used to set up run() to be called on the event.
            //		If 'triggerTopic' is specified, subscribe() is used to set up
            //		run() to be called on the topic.
            if (this.triggerEvent) {
                if (this.trigger) {
                    var scope = dojox.wire.ml._getValue(this.trigger);
                    if (scope) {
                        if (!scope[this.triggerEvent]) {
                            // set a dummy function for an anonymous object
                            scope[this.triggerEvent] = function () {
                            };
                        }
                        this._triggerHandle = dojo.connect(scope, this.triggerEvent, this, "run");
                    }
                } else {
                    var event = this.triggerEvent.toLowerCase();
                    if (event == "onload") {
                        var self = this;
                        dojo.addOnLoad(function () {
                            self._run.apply(self, arguments);
                        });
                    }
                }
            } else if (this.triggerTopic) {
                this._triggerHandle = dojo.subscribe(this.triggerTopic, this, "run");
            }
        },

        _disconnect:function () {
            // summary:
            //		Disconnect run() method from an event or a topic
            // description:
            //		If 'triggerEvent' and 'trigger' are specified, disconnect() is
            //		used to set up run() not to be called on the event.
            //		If 'triggerTopic' is specified, unsubscribe() is used to set up
            //		run() not to be called on the topic.
            if (this._triggerHandle) {
                if (this.triggerTopic) {
                    dojo.unsubscribe(this.triggerTopic, this._triggerHandle);
                } else {
                    dojo.disconnect(this._triggerHandle);
                }
            }
        },

        run:function () {
            // summary:
            //		Run a task
            // description:
            //		This method calls filter() method of child ActionFilter
            //		widgets.
            //		If one of them returns false, this method returns.
            //		Otherwise, _run() method is called.
            var children = this.getChildren();
            for (var i in children) {
                var child = children[i];
                if (child instanceof dojox.wire.ml.ActionFilter) {
                    if (!child.filter.apply(child, arguments)) {
                        return;
                    }
                }
            }
            this._run.apply(this, arguments);
        },

        _run:function () {
            // summary:
            //		Call run() methods of child Action widgets
            // description:
            //		If this widget has child Action widgets, their run() methods
            //		are called.
            var children = this.getChildren();
            for (var i in children) {
                var child = children[i];
                if (child instanceof dojox.wire.ml.Action) {
                    child.run.apply(child, arguments);
                }
            }
        },

        uninitialize:function () {
            // summary:
            //		Over-ride of base widget unitialize function to do some connection cleanup.
            this._disconnect();
            return true;
        }
    });

    dojo.declare("dojox.wire.ml.ActionFilter", dijit._Widget, {
        // summary:
        //		A widget to define a filter for the parent Action to run
        // description:
        //		This base class checks a required property specified with
        //		'required' attribute.
        //		If 'message' is specified, the message is set to a property
        //		specified with 'error'.
        //		Subclasses may implement their own filter() method.
        // required:
        //		A property required
        // requiredValue:
        //		Optional.  A specific value the property is required to have.  If this isn't provided
        //		than any non-false/non-null value of the required propery will cause this filter
        //		to pass.
        // type:
        //		Optional.  A specific type to compare the values as (if requiredValue is set)
        //		Valid values for type are boolean, int, string.  Default is string.
        // message:
        //		An error message to emit if the filter doesn't execute due to property mismatch.
        // error:
        //		A property to store an error due to property mismatch.
        required:"",
        requiredValue:"",
        type:"",
        message:"",
        error:"",


        filter:function () {
            // summary:
            //		Check if a required property is specified.  Also, if provided, check to see
            //		if the required property contains a specific value.
            // description:
            //		If a value is undefined for a property, specified with
            //		'required', this method returns false.
            //		If the value for a property is defined, but there isn't a requiredValue for it
            //		then any non-false value will cause the method to return true.
            //		if requiredValue is set, then filter compares that value with the value from
            //		the required property and returns true if and only if they match.
            //		The type option just allows for a way to convert the required property values
            //		into a proper form for comparison (boolean, number, etc).
            //		If 'message' is specified, it is set to a proeprty specified
            //		with 'error' or shown with alert().
            //		If 'required' starts with "arguments", a property of
            //		the method arguments are checked.
            // returns:
            //		True if a required property is specified (and if requiredValue is specified,
            //		that they match), otherwise false
            if (this.required === "") {
                return true; //Boolean
            } else {
                var value = dojox.wire.ml._getValue(this.required, arguments);
                if (this.requiredValue === "") {
                    //Just see if there's a value, nothing to compare it to.
                    if (value) {
                        return true; //Boolean
                    }
                } else {
                    //See if we need to type convert.
                    var reqValue = this.requiredValue;
                    if (this.type !== "") {
                        var lType = this.type.toLowerCase();
                        if (lType === "boolean") {
                            if (reqValue.toLowerCase() === "false") {
                                reqValue = false;
                            } else {
                                reqValue = true;
                            }
                        } else if (lType === "number") {
                            reqValue = parseInt(reqValue, 10);
                        }
                    }
                    if (value === reqValue) {
                        return true; //boolean
                    }
                }
            }

            if (this.message) {
                if (this.error) {
                    dojox.wire.ml._setValue(this.error, this.message);
                } else {
                    alert(this.message);
                }
            }
            return false; //Boolean
        }
    });

});
